<!-- 文章发布 -->
<template>
  <div class="upload-article">
    <div class="load" v-if="isLoad">
      <div class="typewriter">
        <div class="slide"><i></i></div>
        <div class="paper"></div>
        <div class="keyboard"></div>
      </div>
      <p>文章上传中</p>
    </div>

    <div id="aiEditor" style="padding: 0; margin: 0" ref="divRef">
      <div class="aie-container" style="background-color: #f3f4f6">
        <div class="aie-header-panel">
          <div
            class="aie-container-header"
            style="
              background: #fff;
              display: flex;
              justify-content: center !important;
              width: 100%;
            "
          ></div>
        </div>
        <div class="aie-main">
          <div class="aie-directory-content">
            <div class="aie-directory">
              <h5>文档目录</h5>
              <div id="outline"></div>
            </div>
          </div>
          <div class="aie-container-panel">
            <div class="aie-container-main"></div>
          </div>
          <div class="aie-directory-send">
            <div class="aie-send">
              <div class="article-title">
                <p>发布文章</p>
              </div>
              <div class="article-form">
                <el-form
                  label-width="90px"
                  ref="ruleFormRef"
                  :model="form"
                  :rules="rules"
                  status-icon
                >
                  <el-form-item label="文章标题:" prop="title">
                    <el-input
                      placeholder="请输入文章标题"
                      v-model="form.title"
                    ></el-input>
                  </el-form-item>
                  <el-form-item label="选择分类:" prop="categoryId">
                    <el-radio-group v-model="form.categoryId">
                      <el-radio
                        :value="item.id"
                        :label="item.id"
                        v-for="item in articleCategort"
                        :key="item.id"
                      >
                        {{ item.name }}
                      </el-radio>
                    </el-radio-group>
                  </el-form-item>
                  <el-form-item label="添加标签:">
                    <el-select
                      placeholder="请添加标签"
                      v-model="form.tagIds"
                      multiple
                    >
                      <el-option
                        :value="item.id"
                        :label="item.name"
                        v-for="item in articleTags"
                        :key="item.id"
                      />
                      <template #footer>
                        <el-button v-if="isAdding" text bg size="small">
                          Add an option
                        </el-button>
                        <template v-else>
                          <el-input
                            class="option-input"
                            placeholder="input option name"
                            size="small"
                          />
                          <el-button
                            type="primary"
                            size="small"
                            style="margin: 5px 5px 0 0"
                          >
                            confirm
                          </el-button>
                          <el-button size="small" style="margin: 5px 5px 0 0"
                            >cancel</el-button
                          >
                        </template>
                      </template>
                    </el-select>
                  </el-form-item>
                  <el-form-item label="文章封面:">
                    <el-upload
                      action="#"
                      list-type="picture-card"
                      :auto-upload="false"
                      :limit="1"
                      :class="{ 'hide-upload-btn': !uploadVisible }"
                      :on-remove="handleRemove"
                      ref="uploadRef"
                      v-model:file-list="fileList"
                    >
                      <el-icon><Plus /></el-icon>

                      <template #file="{ file }">
                        <div>
                          <img
                            class="el-upload-list__item-thumbnail"
                            :src="file.url"
                            alt=""
                            style="width: 100%; height: 100%"
                          />
                          <span class="el-upload-list__item-actions">
                            <span
                              class="el-upload-list__item-preview"
                              @click="handlePictureCardPreview(file)"
                            >
                              <el-icon><zoom-in /></el-icon>
                            </span>
                            <span
                              v-if="!disabled"
                              class="el-upload-list__item-delete"
                              @click="handleDownload(file)"
                            >
                              <el-icon><Download /></el-icon>
                            </span>
                            <span
                              v-if="!disabled"
                              class="el-upload-list__item-delete"
                              @click="handleRemove(file)"
                            >
                              <el-icon><Delete /></el-icon>
                            </span>
                          </span>
                        </div>
                      </template>
                    </el-upload>

                    <el-dialog v-model="dialogVisible">
                      <img w-full :src="dialogImageUrl" alt="Preview Image" />
                    </el-dialog>
                  </el-form-item>
                  <el-form-item label="文章摘要:" prop="articleAbstract">
                    <el-input
                      style="width: 235px"
                      :rows="2"
                      type="textarea"
                      placeholder="请输入文章摘要"
                      maxlength="100"
                      show-word-limit
                      v-model="form.articleAbstract"
                    />
                  </el-form-item>
                </el-form>
                <div class="handler">
                  <div class="save" @click="save">保存草稿</div>
                  <div class="submit" @click="submit(ruleFormRef)">
                    确认并发布
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="aie-container-footer"></div>
      </div>
    </div>

    <el-dialog v-model="dialogVisible">
      <img
        w-full
        :src="dialogImageUrl"
        alt="Preview Image"
        style="width: 100%; height: 100%"
      />
    </el-dialog>
  </div>
</template>

<script setup>
import { AiEditor } from "aieditor";
import "aieditor/dist/style.css";
import { ElMessage } from "element-plus";
import { ref, onMounted, computed } from "vue";
import {
  reqQueryTag,
  reqQueryCategory,
  reqAdd,
  reqQueryDetail,
  reqUpdate,
} from "@/api/article";
import { reqFileUpload } from "@/api/common";
import { useRoute, useRouter } from "vue-router";
import useUserStore from "@/store/modules/user";

const userStore = useUserStore();

const isLoad = ref(false);

const $route = useRoute();
const $router = useRouter();

let divRef = ref();
let aiEditor = null;

// 表单实例
const ruleFormRef = ref();

// 文章表单数据
const form = ref({
  title: "",
  content: "",
  categoryId: "",
  tagIds: [],
  articleUrl: "",
  articleAbstract: "",
});

// 文章分类
const articleCategort = ref([]);

// 文章标签
const articleTags = ref([]);

const fileList = ref([]);

onMounted(() => {
  // 初始化编辑器
  aiEditor = new AiEditor({
    element: divRef.value,
    placeholder: "点击输入内容...",
    content: "",
    onCreated: (editor) => {
      updateOutLine(editor);
    },
    onChange: (editor) => {
      updateOutLine(editor);
    },
  });

  getCategory();
  getTags();

  // 编辑状态
  if ($route.query.articleId) {
    const entity = {
      id: $route.query.articleId,
    };
    reqQueryDetail(entity).then((res) => {
      console.log(res);
      if (res.code == 200) {
        // form.value = Object.assign(form.value, res.data);

        form.value.title = res.data.title;
        form.value.categoryId = res.data.categoryId;
        form.value.tagIds = res.data.tagIds;
        form.value.articleAbstract = res.data.articleAbstract;
        form.value.articleUrl = res.data.articleUrl;
        aiEditor.setContent(res.data.content);

        fileList.value.push({ url: res.data.articleUrl });
      }
    });
  }
});

// 表单校验规则
const rules = ref({
  title: [{ required: true, message: "请输入文章标题", trigger: "blur" }],
  categoryId: [
    { required: true, message: "请选择文章分类", trigger: "change" },
  ],
  articleAbstract: [
    { required: true, message: "请输入文章摘要", trigger: "blur" },
  ],
});

// 获取文章分类
const getCategory = () => {
  reqQueryCategory().then((res) => {
    if (res.code == 200) {
      articleCategort.value = res.data;
    }
  });
};

// 获取文章标签
const getTags = () => {
  reqQueryTag().then((res) => {
    if (res.code == 200) {
      articleTags.value = res.data;
    }
  });
};

// 保存草稿
const save = () => {
  form.value.status = 0;
  if (aiEditor.isEmpty()) {
    ElMessage.warning("内容不能为空");
  }
  form.value.content = aiEditor.getHtml();
  reqAdd(form.value).then((res) => {
    if (res.code == 200) {
      ElMessage.success("草稿保存成功");
    } else {
      ElMessage.error("草稿保存失败");
    }
  });
  resetForm(ruleFormRef.value);
};

// 发布文章
const submit = async (formEl) => {
  // 判断用户是否登录
  if (!userStore.userName) {
    ElMessage.error("用户未登录");
    return;
  }

  if (!formEl) return;

  // 参数校验
  await formEl.validate(async (valid, fields) => {
    if (valid) {
      // 判断文章是否有内容
      if (aiEditor.isEmpty()) {
        ElMessage.info("请输入文章内容");
        return;
      } else {
        form.value.content = aiEditor.getHtml();
      }

      // 处理文件上传
      if (fileList.value.length > 0 && fileList.value[0].raw) {
        // 调取上传文件的接口，拿到路径保存到form中
        const formData = new FormData();
        formData.append("file", fileList.value[0].raw);

        try {
          const res = await reqFileUpload(formData);
          if (res.code == 200) {
            form.value.articleUrl = res.data;
          } else {
            ElMessage.error("文件上传失败");
            return;
          }
        } catch (error) {
          ElMessage.error("文件上传时发生错误");
          return;
        }
      }
      isLoad.value = true;
      // 文件上传成功或者没有文件需要上传
      await addOrUpload();
    }
  });
};

// 新增或修改操作
const addOrUpload = () => {
  if ($route.query.articleId) {
    // 修改操作
    form.value.id = $route.query.articleId;
    reqUpdate(form.value).then((res) => {
      if (res.code == 200) {
        ElMessage.success("修改成功");
        isLoad.value = false;
        $router.go(-1);
      } else {
        ElMessage.success("修改失败");
        isLoad.value = false;
      }
    });
  } else {
    // 新增操作
    reqAdd(form.value).then((res) => {
      if (res.code == 200) {
        ElMessage.success("发布成功");
        isLoad.value = false;
      } else {
        ElMessage.success("发布失败");
        isLoad.value = false;
      }
    });
  }
  resetForm(ruleFormRef.value);
};

// 重置表单
const resetForm = (formEl) => {
  if (!formEl) return;
  form.value = {
    title: "",
    content: "",
    categoryId: "",
    tagIds: [],
    articleUrl: "",
    articleAbstract: "",
  };
  aiEditor.clear();
  formEl.resetFields();
  fileList.value = [];
};

// 更新目录
const updateOutLine = (editor) => {
  const outlineContainer = document.querySelector("#outline");
  while (outlineContainer?.firstChild) {
    outlineContainer.removeChild(outlineContainer.firstChild);
  }

  const outlines = editor.getOutline();
  for (let outline of outlines) {
    const child = document.createElement("div");
    child.classList.add(`aie-title${outline.level}`);
    child.style.marginLeft = `${14 * (outline.level - 1)}px`;
    child.innerHTML = `<a href="#${outline.id}">${outline.text}</a>`;
    child.addEventListener("click", (e) => {
      e.preventDefault();
      const el = editor.innerEditor.view.dom.querySelector(`#${outline.id}`);
      el.scrollIntoView({
        behavior: "smooth",
        block: "center",
        inline: "nearest",
      });
      setTimeout(() => {
        editor.focusPos(outline.pos + outline.size - 1);
      }, 1000);
    });
    outlineContainer?.appendChild(child);
  }
};

// 上传文件的实例
const uploadRef = ref();

//上传按钮是否显示
const uploadVisible = computed(() => !fileList.value.length);

// 清除文件
const handleRemove = (file) => {
  fileList.value = fileList.value.filter((item) => item !== file);
};

const dialogImageUrl = ref();
const dialogVisible = ref(false);
// 文件放大
const handlePictureCardPreview = (file) => {
  dialogImageUrl.value = file.url;
  dialogVisible.value = true;
};
// 文件下载
const handleDownload = (file) => {
  console.log(file);
};
</script>

<style scoped lang="scss">
.upload-article {
  width: 100%;
  min-height: calc(100vh - 55px);
}
.page-header {
  background-color: #fff;
  height: 50px;
  line-height: 50px;
  padding: 0 2rem;
  display: flex;
  position: sticky;
  border-bottom: 1px solid #efefef;
  top: 0;
  z-index: 1;
}

.aie-header-panel {
  position: sticky;
  top: 51px;
  z-index: 1;
}

.aie-header-panel aie-header > div {
  align-items: center;
  justify-content: center;
  padding: 10px 0;
}

.aie-container {
  border: none !important;
}

.aie-container-panel {
  width: calc(100% - 2rem - 2px);
  max-width: 800px;
  margin: 1rem auto;
  border: 1px solid rgb(229 231 235);
  background-color: #fff;
  height: 100%;
  min-height: 100vh;
  padding: 1rem;
  z-index: 99;
}

.aie-main {
  position: relative;
}

.aie-directory {
  position: absolute;
  top: 30px;
  left: 10px;
  width: 260px;
  z-index: 0;
}

.aie-directory h5 {
  color: #000000c4;
  font-size: 16px;
  text-indent: 4px;
  line-height: 32px;
}

.aie-directory a {
  height: 30px;
  font-size: 14px;
  color: #000000a3;
  text-indent: 4px;
  line-height: 30px;
  text-decoration: none;
  width: 100%;
  display: inline-block;
  margin: 0;
  padding: 0;
  white-space: nowrap;
  overflow: hidden;
  -o-text-overflow: ellipsis;
  text-overflow: ellipsis;
}

.aie-directory a:hover {
  cursor: pointer;
  background-color: #334d660f;
  border-radius: 4px;
}

.aie-title1 {
  font-size: 14px;
  font-weight: 500;
}

.aie-title2,
.aie-title3,
.aie-title4,
.aie-title5,
.aie-title6 {
  font-size: 12px;
}

.aie-directory-content {
  position: sticky;
  top: 108px;
}

@media screen and (max-width: 1280px) {
  .aie-directory {
    display: none;
  }
}

@media screen and (max-width: 1400px) {
  .aie-directory {
    width: 200px;
  }
}

.aie-send {
  position: fixed;
  top: 108.5px;
  right: 10px;
  width: 380px;
  z-index: 0;
  background-color: #fff;
  height: 550px;
  .article-title {
    width: 100%;
    height: 10%;
    box-sizing: border-box;
    color: #505050;
    font-weight: bold;
    border-bottom: 1px solid #ccc;
    display: flex;
    align-items: center;
    p {
      padding: 0 0 0 10px;
    }
  }
  .article-form {
    width: 100%;
    height: 80%;
    padding: 10px;
    box-sizing: border-box;
    margin-top: 20px;
  }
  .handler {
    width: 100%;
    height: 10%;
    display: flex;
    justify-content: end;
    font-size: 12px;

    .save,
    .submit {
      border: 1px solid #207bd6;
      height: 30px;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 5px 10px;
      box-sizing: border-box;
      border-radius: 5px;
      color: #207bd6;
      cursor: pointer;
    }
    .submit {
      margin-left: 10px;
      color: #fff;
      background-color: #207bd6;
    }
    .submit:hover {
      background-color: #388ce0;
    }
  }
}

.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
  border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  text-align: center;
}

.avatar-uploader .avatar {
  width: 178px;
  height: 178px;
  display: block;
}

/** 隐藏上传按钮 */
.hide-upload-btn :deep(.el-upload--picture-card) {
  display: none;
}

.load {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 998;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  p {
    margin: 10px 0;
    color: #fff;
    background-color: #cccccc13;
    padding: 10px;
    box-sizing: border-box;
    border-radius: 5px;
  }
  .typewriter {
    --blue: #5c86ff;
    --blue-dark: #275efe;
    --key: #fff;
    --paper: #eef0fd;
    --text: #d3d4ec;
    --tool: #fbc56c;
    --duration: 3s;
    position: relative;
    -webkit-animation: bounce05 var(--duration) linear infinite;
    animation: bounce05 var(--duration) linear infinite;
  }

  .typewriter .slide {
    width: 92px;
    height: 20px;
    border-radius: 3px;
    margin-left: 14px;
    transform: translateX(14px);
    background: linear-gradient(var(--blue), var(--blue-dark));
    -webkit-animation: slide05 var(--duration) ease infinite;
    animation: slide05 var(--duration) ease infinite;
  }

  .typewriter .slide:before,
  .typewriter .slide:after,
  .typewriter .slide i:before {
    content: "";
    position: absolute;
    background: var(--tool);
  }

  .typewriter .slide:before {
    width: 2px;
    height: 8px;
    top: 6px;
    left: 100%;
  }

  .typewriter .slide:after {
    left: 94px;
    top: 3px;
    height: 14px;
    width: 6px;
    border-radius: 3px;
  }

  .typewriter .slide i {
    display: block;
    position: absolute;
    right: 100%;
    width: 6px;
    height: 4px;
    top: 4px;
    background: var(--tool);
  }

  .typewriter .slide i:before {
    right: 100%;
    top: -2px;
    width: 4px;
    border-radius: 2px;
    height: 14px;
  }

  .typewriter .paper {
    position: absolute;
    left: 24px;
    top: -26px;
    width: 40px;
    height: 46px;
    border-radius: 5px;
    background: var(--paper);
    transform: translateY(46px);
    -webkit-animation: paper05 var(--duration) linear infinite;
    animation: paper05 var(--duration) linear infinite;
  }

  .typewriter .paper:before {
    content: "";
    position: absolute;
    left: 6px;
    right: 6px;
    top: 7px;
    border-radius: 2px;
    height: 4px;
    transform: scaleY(0.8);
    background: var(--text);
    box-shadow: 0 12px 0 var(--text), 0 24px 0 var(--text), 0 36px 0 var(--text);
  }

  .typewriter .keyboard {
    width: 120px;
    height: 56px;
    margin-top: -10px;
    z-index: 1;
    position: relative;
  }

  .typewriter .keyboard:before,
  .typewriter .keyboard:after {
    content: "";
    position: absolute;
  }

  .typewriter .keyboard:before {
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    border-radius: 7px;
    background: linear-gradient(135deg, var(--blue), var(--blue-dark));
    transform: perspective(10px) rotateX(2deg);
    transform-origin: 50% 100%;
  }

  .typewriter .keyboard:after {
    left: 2px;
    top: 25px;
    width: 11px;
    height: 4px;
    border-radius: 2px;
    box-shadow: 15px 0 0 var(--key), 30px 0 0 var(--key), 45px 0 0 var(--key),
      60px 0 0 var(--key), 75px 0 0 var(--key), 90px 0 0 var(--key),
      22px 10px 0 var(--key), 37px 10px 0 var(--key), 52px 10px 0 var(--key),
      60px 10px 0 var(--key), 68px 10px 0 var(--key), 83px 10px 0 var(--key);
    -webkit-animation: keyboard05 var(--duration) linear infinite;
    animation: keyboard05 var(--duration) linear infinite;
  }

  @keyframes bounce05 {
    85%,
    92%,
    100% {
      transform: translateY(0);
    }

    89% {
      transform: translateY(-4px);
    }

    95% {
      transform: translateY(2px);
    }
  }

  @keyframes slide05 {
    5% {
      transform: translateX(14px);
    }

    15%,
    30% {
      transform: translateX(6px);
    }

    40%,
    55% {
      transform: translateX(0);
    }

    65%,
    70% {
      transform: translateX(-4px);
    }

    80%,
    89% {
      transform: translateX(-12px);
    }

    100% {
      transform: translateX(14px);
    }
  }

  @keyframes paper05 {
    5% {
      transform: translateY(46px);
    }

    20%,
    30% {
      transform: translateY(34px);
    }

    40%,
    55% {
      transform: translateY(22px);
    }

    65%,
    70% {
      transform: translateY(10px);
    }

    80%,
    85% {
      transform: translateY(0);
    }

    92%,
    100% {
      transform: translateY(46px);
    }
  }

  @keyframes keyboard05 {
    5%,
    12%,
    21%,
    30%,
    39%,
    48%,
    57%,
    66%,
    75%,
    84% {
      box-shadow: 15px 0 0 var(--key), 30px 0 0 var(--key), 45px 0 0 var(--key),
        60px 0 0 var(--key), 75px 0 0 var(--key), 90px 0 0 var(--key),
        22px 10px 0 var(--key), 37px 10px 0 var(--key), 52px 10px 0 var(--key),
        60px 10px 0 var(--key), 68px 10px 0 var(--key), 83px 10px 0 var(--key);
    }

    9% {
      box-shadow: 15px 2px 0 var(--key), 30px 0 0 var(--key),
        45px 0 0 var(--key), 60px 0 0 var(--key), 75px 0 0 var(--key),
        90px 0 0 var(--key), 22px 10px 0 var(--key), 37px 10px 0 var(--key),
        52px 10px 0 var(--key), 60px 10px 0 var(--key), 68px 10px 0 var(--key),
        83px 10px 0 var(--key);
    }

    18% {
      box-shadow: 15px 0 0 var(--key), 30px 0 0 var(--key), 45px 0 0 var(--key),
        60px 2px 0 var(--key), 75px 0 0 var(--key), 90px 0 0 var(--key),
        22px 10px 0 var(--key), 37px 10px 0 var(--key), 52px 10px 0 var(--key),
        60px 10px 0 var(--key), 68px 10px 0 var(--key), 83px 10px 0 var(--key);
    }

    27% {
      box-shadow: 15px 0 0 var(--key), 30px 0 0 var(--key), 45px 0 0 var(--key),
        60px 0 0 var(--key), 75px 0 0 var(--key), 90px 0 0 var(--key),
        22px 12px 0 var(--key), 37px 10px 0 var(--key), 52px 10px 0 var(--key),
        60px 10px 0 var(--key), 68px 10px 0 var(--key), 83px 10px 0 var(--key);
    }

    36% {
      box-shadow: 15px 0 0 var(--key), 30px 0 0 var(--key), 45px 0 0 var(--key),
        60px 0 0 var(--key), 75px 0 0 var(--key), 90px 0 0 var(--key),
        22px 10px 0 var(--key), 37px 10px 0 var(--key), 52px 12px 0 var(--key),
        60px 12px 0 var(--key), 68px 12px 0 var(--key), 83px 10px 0 var(--key);
    }

    45% {
      box-shadow: 15px 0 0 var(--key), 30px 0 0 var(--key), 45px 0 0 var(--key),
        60px 0 0 var(--key), 75px 0 0 var(--key), 90px 2px 0 var(--key),
        22px 10px 0 var(--key), 37px 10px 0 var(--key), 52px 10px 0 var(--key),
        60px 10px 0 var(--key), 68px 10px 0 var(--key), 83px 10px 0 var(--key);
    }

    54% {
      box-shadow: 15px 0 0 var(--key), 30px 2px 0 var(--key),
        45px 0 0 var(--key), 60px 0 0 var(--key), 75px 0 0 var(--key),
        90px 0 0 var(--key), 22px 10px 0 var(--key), 37px 10px 0 var(--key),
        52px 10px 0 var(--key), 60px 10px 0 var(--key), 68px 10px 0 var(--key),
        83px 10px 0 var(--key);
    }

    63% {
      box-shadow: 15px 0 0 var(--key), 30px 0 0 var(--key), 45px 0 0 var(--key),
        60px 0 0 var(--key), 75px 0 0 var(--key), 90px 0 0 var(--key),
        22px 10px 0 var(--key), 37px 10px 0 var(--key), 52px 10px 0 var(--key),
        60px 10px 0 var(--key), 68px 10px 0 var(--key), 83px 12px 0 var(--key);
    }

    72% {
      box-shadow: 15px 0 0 var(--key), 30px 0 0 var(--key),
        45px 2px 0 var(--key), 60px 0 0 var(--key), 75px 0 0 var(--key),
        90px 0 0 var(--key), 22px 10px 0 var(--key), 37px 10px 0 var(--key),
        52px 10px 0 var(--key), 60px 10px 0 var(--key), 68px 10px 0 var(--key),
        83px 10px 0 var(--key);
    }

    81% {
      box-shadow: 15px 0 0 var(--key), 30px 0 0 var(--key), 45px 0 0 var(--key),
        60px 0 0 var(--key), 75px 0 0 var(--key), 90px 0 0 var(--key),
        22px 10px 0 var(--key), 37px 12px 0 var(--key), 52px 10px 0 var(--key),
        60px 10px 0 var(--key), 68px 10px 0 var(--key), 83px 10px 0 var(--key);
    }
  }
}
</style>